home *** CD-ROM | disk | FTP | other *** search
- /*
- File: ShellMem.cpp
-
- Contains: Memory management for the Shell
-
- Owned by: Nick Pilch
-
- Copyright: © 1995 - 1997 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- <15> 1/9/97 TJ Fixed decleration of size ptrs.
- <14> 1/8/97 DH 1401434: Make out-of-temp mem non fatal
- (user can cancel quit. Properly notify user
- of different out-of-mem situations.
- <13> 12/19/96 TJ Fixed decleration of some size prtrs.
- <12> 12/18/96 DH 1375780,1612023: Handled more cases of low
- memory (especially Read Only docs) and
- changed how we deal with others
- (out-of-temp mem is no longer always
- fatal).
- <11> 10/22/96 DH 1336808 ,1333949,1368888,1375780 Radically
- changed low mem handling. We now check for
- low memory thresholds; warn the user when
- space is low; allow them to adjust the doc
- heap size if app space is low; close the
- document if we run out of memory. Low-mem
- dialogs have also been completely
- redesigned, and display accurate
- information about the low-memory situation,
- including recommendations to user on how to
- deal with them.
- <10> 27.09.1996 NP 1385775: Incorrect dialog after OD
- initialization fails.
- <9> 9/19/96 DH Task: low memory changes. 1377922, 1377888.
- Low-mem threshold change. Also, we now exit
- when we are very low on memory.
- <8> 7/30/96 DH Changed unknown macro/function BREAK to
- WARN.
- <7> 7/30/96 eeh 1372943: add ODShellCorruptDocGoodbye and
- ODShellGenericGoodbye
- <6> 7/28/96 DH Bug#1372954: Changed LowMemoryAlert to call
- ExitToShell instead of ODCloseDocument.
- This was done because that utility routine
- can crash under low memory situations.
- <5> 6/21/96 jpa T10002: Lowered min app heap free space to
- 32k, 6k contig.
- <4> .04.1996 NP 1339832: Send exit event before closing
- documents/windows from LowMemoryAlert.
- <3> 4/4/96 NP 1338241: Unstaticize function.
- <2> 3/29/96 DM 1334273: show low mem quit alert and then
- exit to shell when unable to show another
- low mem alert due to out-of-mem err
- <1> 10/24/95 jpa first checked in
-
- In Progress:
-
- */
-
-
- #ifndef _RLSHELL_
- #include "RlShell.h"
- #endif
-
- #ifndef _SHELLDEF_
- #include "ShellDef.h"
- #endif
-
- #ifndef _DISPTCH_
- #include <Disptch.xh>
- #endif
-
- #ifndef _MEMMGR_
- #include <MemMgr.h>
- #endif
-
- #ifndef _DLOGUTIL_
- #include <DlogUtil.h>
- #endif
-
- #ifndef _DOCUTILS_
- #include <DocUtils.h>
- #endif
-
- #ifndef _USERSRCM_
- #include <UseRsrcM.h>
- #endif
-
- #ifndef _TEMPITER_
- #include <TempIter.h>
- #endif
-
- #ifndef _SHELLMEM_
- #include <ShellMem.h>
- #endif
-
- #ifndef SOM_ODWindowState_xih
- #include <WinStat.xh>
- #endif
-
- #ifndef __ICONS__
- #include <Icons.h>
- #endif
-
- //==============================================================================
- // CONSTANTS
- //==============================================================================
-
- const size_t kSlushFundSize = 10 * 1024; // Size of memory slush-fund
- const size_t kSlushFundAllocLimit = 2 * 1024; // Max request that will free slush fund
-
- const ODSize kGoodAppFreeSpace = 94 * 1024; // Sizes below which we notify the user
- const ODSize kGoodAppContigSpace = 32 * 1024;
- const ODSize kGoodTempFreeSpace = 128 * 1024;
- const ODSize kGoodTempContigSpace = 64 * 1024;
- const ODSize kBailAppFreeSpace = 2 * 1024; // App-heap mem requirement of low-mem alert
-
- // This should be moved to ShellDef.h.
- const short cancelButtonID = 2;
-
- //==============================================================================
- // FUNCTION DEFINITIONS
- //==============================================================================
-
- static ODBoolean DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document );
-
- //==============================================================================
- // METHODS
- //==============================================================================
-
- //-------------------------------------------------------------------------------------
- // InitMemory
- //-------------------------------------------------------------------------------------
-
- void
- RealShell::InitMemory( )
- {
- // Preload low-mem alerts so we'll be able to use them when space is low:
- CUsingLibraryResources r;
- ::Get1Resource('ALRT',kSHLphTempMemIsLow);
- ::Get1Resource('DITL',kSHLphTempMemIsLow);
- ::Get1Resource('DLOG',kSHLphTempMemIsLowFatal);
- ::Get1Resource('DITL',kSHLphTempMemIsLowFatal);
- ::Get1Resource('ALRT',kSHLphAppSpaceIsLow);
- ::Get1Resource('DITL',kSHLphAppSpaceIsLow);
- ::Get1Resource('ALRT',kSHLphAppSpaceIsLowRO);
- ::Get1Resource('DITL',kSHLphAppSpaceIsLowRO);
- ::Get1Resource('DLOG',kSHLphAppSpaceIsLowDlg);
- ::Get1Resource('DITL',kSHLphAppSpaceIsLowDlg);
-
- if( ! MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit) )
- WARN("Could not allocate slush fund");
- }
-
-
- //-------------------------------------------------------------------------------------
- // Purge
- //-------------------------------------------------------------------------------------
-
- ODSize RealShell::Purge(ODSize size)
- {
- ODSize result;
- TRY{
- result= fSession->Purge(fEV,size);
- }CATCH_ALL{
- result = 0;
- // do not reraise, ignore exception
- }ENDTRY
- return result;
- }
-
-
- //-------------------------------------------------------------------------------------
- // IsFreeMemoryLow
- //-------------------------------------------------------------------------------------
-
- inline ODSLong Max( ODSLong a, ODSLong b )
- {return a>b ?a :b;}
-
- ODSize RealShell::IsFreeMemoryLow( ODBoolean &appIsLow, ODBoolean &tempIsLow )
- {
- // First check/replenish the memory slush fund:
- ODBoolean slushy = kODFalse;
- if( MMSlushFundSize(kDefaultHeapID) > 0 )
- slushy = kODTrue;
- else
- slushy = MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit);
-
- size_t totalFree = 0,contig = 0;
- ODSLong freeDelta = 0, contigDelta = 0;
- ODSLong purgeApp = 0, purgeTemp = 0;
-
- MMSystemFreeSpace(kMMAppMemory, &totalFree,&contig);
-
- freeDelta = kGoodAppFreeSpace-totalFree;
- contigDelta = kGoodAppContigSpace-contig;
- purgeApp = Max(freeDelta,contigDelta);
- // appIsLow = (purgeApp>0);
- if( totalFree<kGoodAppFreeSpace || contig<kGoodAppContigSpace )
- appIsLow = 1;
-
- MMSystemFreeSpace(kMMTempMemory, &totalFree,&contig);
- freeDelta = kGoodTempFreeSpace-totalFree;
- contigDelta = kGoodTempContigSpace-contig;
- purgeTemp = Max(freeDelta,contigDelta);
- // tempIsLow = (purgeTemp>0);
- if( totalFree<kGoodTempFreeSpace || contig<kGoodTempContigSpace )
- tempIsLow = 1;
-
- if( !slushy )
- tempIsLow = kODTrue;
-
- purgeApp = Max(purgeApp,purgeTemp);
- return (purgeApp>0 ?purgeApp :0);
- }
-
-
- //-------------------------------------------------------------------------------------
- // CheckFreeMemory
- //-------------------------------------------------------------------------------------
-
- ODBoolean RealShell::CheckFreeMemory( )
- {
- // If space is low, purge. If space is still low, alert the user, provided this
- // process is active. After the user is alerted, set a flag so we don't put up
- // an endless stream of alerts. Note that we don't use the normal dialog filter,
- // since it calls the Dispatcher, which may cause trouble when space is low.
-
- CUsingLibraryResources rez;
-
- ODBoolean appIsLow = kODFalse, tempIsLow = kODFalse;
- ODSize purge = this->IsFreeMemoryLow(appIsLow,tempIsLow);
-
- if( purge ) {
- // Low on free space, so purge:
- this->Purge( 2*purge );
-
- this->IsFreeMemoryLow(appIsLow,tempIsLow);
-
- if( (appIsLow || tempIsLow) ) {
- // Yow, still low on memory after purging.
- size_t freeApp=0,contigApp=0, freeTemp=0,contigTemp = 0;
- MMSystemFreeSpace(kMMAppMemory, &freeApp,&contigApp);
- MMSystemFreeSpace(kMMTempMemory, &freeTemp,&contigTemp);
-
- // If app heap memory gets this low, OpenDoc cannot reliably function. Sorry, but
- // we must exit the scene.
- if( freeApp<kGoodAppFreeSpace/2 || contigApp<kGoodAppContigSpace/2 )
- {
- // But first, allow the user to increase the heapsize of the document so they
- // will have more memory next time to work with that document.
- this->LowAppMemoryAlert( kODTrue ); // Fatal low-mem alert.
- ::ExitToShell(); // Yikes!! Does this ever blow!
- }
- // Likewise with temp mem, we must quit if temp mem space gets too low. However, we
- // will allow the user to cancel this quitting and switch to the Finder to free up memory.
- if( (freeTemp<kGoodTempFreeSpace/2) || (contigTemp<kGoodTempContigSpace/2) )
- {
- if( !fOutOfTempMemNotified )
- {
- ODShellLowTempMemoryGoodbye();
- fOutOfTempMemNotified = kODTrue; // Only notify user 1st time. Otherwise
- // they get an endless sequence of dialogs.
- }
- return kODFalse;
- }
- // Otherwise, if temp-mem is low, let the user know that closing docs/apps
- // can free up memory to allow the document to be reopened safely.
- else
- {
- // We are in a low memory state here, so notify user of the particular condition,
- // including options to change the memory size, save or close the document.
- if( fProcessIsActive && ( freeApp > kBailAppFreeSpace ) )
- {
- if( ((freeApp < kGoodAppFreeSpace) || (contigApp < kGoodAppContigSpace))
- && !fLowAppMemNotified )
- {
- this->LowAppMemoryAlert( kODFalse );
- fLowAppMemNotified = kODTrue; // Only notify user 1st time. Otherwise
- } // they get an endless sequence of dialogs.
- if( ((freeTemp < kGoodTempFreeSpace) || (contigTemp < kGoodTempContigSpace))
- && !fLowTempMemNotified )
- {
- this->LowTempMemoryAlert( );
- fLowTempMemNotified = kODTrue; // Only notify user 1st time. Otherwise
- } // they get an endless sequence of dialogs.
- }
- return kODFalse;
- }
- }
- }
- fLowTempMemNotified = kODFalse; // We're okay now, clear notification state
- fLowAppMemNotified = kODFalse;
- fOutOfTempMemNotified = kODFalse;
-
- return kODTrue;
- }
-
-
- static ODBoolean
- DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document )
- {
- // Throws no exceptions.
- TRY{
- return ODDocumentHasChanges(ev,session,document);
- }CATCH_ALL{
- WARN("ODDocumentHasChanges failed, err %d",ErrorCode());
- }ENDTRY
- return kODFalse;
- }
-
- #if ODDebug
- void BREAK( const char[] );
- #endif
-
- static void GoodbyeAlert( short alertID )
- {
- #if ODDebug
- WARN("about to show fatal low memory or related alert...");
- #endif
- GenericAlert(alertID);
- // make sure CUsingLibraryResources goes out of scope first...
- ::ExitToShell(); // bye bye process
- }
-
- void ODShellLowTempMemoryGoodbye()
- {
- // GoodbyeAlert( kSHLphTempMemIsLowFatal );
- #if ODDebug
- WARN("about to show fatal low memory or related alert...");
- #endif
-
- short idClicked = StopAlert( kSHLphTempMemIsLowFatal,kODNULL);
- if( idClicked != cancelButtonID )
- ::ExitToShell(); // bye bye process
- }
-
- void ODShellGenericGoodbye( ODBoolean inform )
- {
- if ( inform )
- GoodbyeAlert( kODAlertShellGenericError );
- else
- ::ExitToShell();
- }
-
- void ODShellCorruptDocGoodbye()
- {
- GoodbyeAlert( kODAlertShellCorruptDocError );
- }
-
- //------------------------------------------------------------------------------
- // DispatchExitEvent
- //
- // Duplicated in ODSessnB.cpp
- //------------------------------------------------------------------------------
-
- static void DispatchExitEvent(Environment* ev, ODDispatcher* dispatcher)
- {
- // Copied from RealShell::FakePrintMenuEvent
- ODEventData event;
- event.message = 0L;
- event.what = kODEvtExit;
- // zero the rest of the fields
- WASSERT( sizeof(Point) == sizeof(long) );
- *(long*)&event.where = 0L;
- event.when = 0L;
- event.modifiers = 0;
- dispatcher->Dispatch(ev, &event);
- }
-
- //------------------------------------------------------------------------------
- // RealShell::LowTempMemoryAlert
- //------------------------------------------------------------------------------
-
- void RealShell::LowTempMemoryAlert( void )
- {
- /* Do not use the regular ODDialogFilter for this alert, as it calls back to the
- Dispatcher, which calls other OpenDoc routines. This could be dangerous/fatal
- in precisely this kind of low memory situation. */
-
- ODSShort result; ODVolatile(result);
- TRY{
- // Shazam! Show the alert:
-
- // $$$$$ dh - Should use ShowAlert instead.
- CautionAlert( kSHLphTempMemIsLow, kODNULL );
- // ShowAlert( GetGlobalEnvironment(),kSHLphTempMemIsLow,kODNULL,fSession );
- }CATCH_ALL{
- if ( ErrorCode() == kODErrOutOfMemory )
- {
- ODShellLowTempMemoryGoodbye(); // goodbye cruel world
- }
- else
- {
- WARN("Error %d showing low mem alert",ErrorCode());
- // don't reraise
- }
- }ENDTRY
- }
-
- //-------------------------------------------------------------------------------------
- // RealShell::LowAppMemoryAlert
- //
- // Displays dialog informing the user that the app heap is low on memory, and that
- // continuing could lead to data loss. It then allows the user to adjust the size of
- // the document, and save and close. The user can also cancel and do other cleanup
- // before quitting.
- //-------------------------------------------------------------------------------------
-
- ODBoolean RealShell::LowAppMemoryAlert( ODBoolean fatalLowMem )
- {
- /*
- There's a lot to do in this routine. We want to display a dialog informing the user
- that the app heap is running low, then allow them to change the doc size, and close
- the file. Here's how it goes:
- • Check if the document is read-only or not. If it is read-only, then we should
- display a dialog merely informing the user that memory is low without any
- controls to change the memory size.
- • Check if the document needs to be saved. If so, inform the user that the
- document will be saved if they decide to change the memory size.
- • Present the user with controls to adjust the memory size.
- • Inform the user that choosing OK will set the size of the doc to the new amount,
- possibly save, then close and reopen the document. Then do it!
- */
- ODBoolean needSave = kODFalse; ODVolatile(needSave);
- ODFacet* sizeFacet = kODNULL; ODVolatile(sizeFacet);
- ODBoolean hasWriteAccess = kODFalse; ODVolatile(hasWriteAccess);
- ODDraft* docDraft = kODNULL; ODVolatile(sizeFacet);
- PlatformFile* docFile = kODNULL; ODVolatile(docFile);
- short buttonHit = 0;
- ODBoolean changeMade = kODFalse; ODVolatile(changeMade);
-
- WASSERT(fSession != kODNULL);
- if (!fSession)
- THROW(kODErrIllegalNullInput);
-
- // ArrowCursor();
-
- // Check if document is read-only. If so, display alert without controls and
- // text that informs user that document should/needs to be closed.
- TRY
- TempODWindow window = fSession->GetWindowState(fEV)->AcquireFrontRootWindow(fEV);
- sizeFacet = window->GetRootFacet(fEV);
- docDraft = ODGetDraftOfWindow(fEV, window);
- hasWriteAccess = ODDraftHasWriteAccess(fEV, docDraft);
- CATCH_ALL
- if ( ErrorCode() == kODErrOutOfMemory )
- ODShellLowTempMemoryGoodbye(); // goodbye cruel world
- else
- { // Since we couldn't successfully check if the doc is read-only,
- // we will pessimistically assume we do not have write access.
- hasWriteAccess = kODFalse;
- WARN("Couldn't check doc permissions because error %d.",ErrorCode());
- // don't reraise, just go on.
- }
- ENDTRY
-
- CUsingLibraryResources duh;
-
- if( !hasWriteAccess )
- {
- if( fatalLowMem )
- CautionAlert( kSHLphAppSpaceIsLowFatalRO, kODNULL);
- else
- CautionAlert( kSHLphAppSpaceIsLowRO, kODNULL);
- }
- else
- {
- // Even if we have a fatal low mem situation, allow the user to change the doc mem size.
- ODError reopenErr = noErr;
-
- TRY
- docFile = GetPlatformFileFromContainer(fEV, docDraft->GetDocument(fEV)->GetContainer(fEV));
- FSSpec docFSSpec = docFile->GetFileSpec();
- ODBoolean changeMade = ChangeDocSize(&docFSSpec, kODTrue, fatalLowMem); // Increase Size
- if( changeMade == kODTrue && fatalLowMem != kODTrue )
- reopenErr = CloseAndReopenDoc(&docFSSpec);
- CATCH_ALL
- // If we ran out of memory trying to do this, then OpenDoc aint getting any better soon!
- // $$$$$ - should we actually quit here? This routine could potentially use a lot
- // of memory, and I'm not sure it's a good idea to close the document
- // if ( ErrorCode() == kODErrOutOfMemory )
- // ODShellLowMemoryGoodbye(); // goodbye cruel world
- // Since we don't know if we changed the size of the doc, pessimistically
- // return that we couldn't.
- changeMade = kODFalse;
- WARN("Couldn't change the size of the doc because error %d.",ErrorCode());
- // don't reraise, just go on.
- ENDTRY
- // }
- }
- if( fatalLowMem)
- ::ExitToShell(); // Now we must die.
-
- return changeMade;
- }
-